<?php

namespace Library\Support;


use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Result;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\QueryBuilder;
use Library\OrmManager;

/**
 * @method DB select(mixed ...$fields)
 * @method DB setParameter(string|int $key, mixed $value, ParameterType|ArrayParameterType|string|int|null $type = null)
 * @method DB where(mixed ...$predicates)
 */
class DB
{
    private static ?DB $instance = null;

    protected QueryBuilder $builder;

    protected string $className;
    protected string $fields;
    protected string $alias;

    /**
     * @var EntityManager
     */
    protected EntityManager $manager;
    protected array $sqlArr = [];
    protected array $prepare = [];
    protected string $sql = '';


    protected EntityRepository $repository;

    protected ?Result $result = null;
    protected array $options = [
        'params'=>[],
        'types'=>""
    ];

    public function __construct(string $className = "", string $alias = "e"){
        $ormManager = new OrmManager();
        $this->className = $className;
        $this->manager = $ormManager->getManager();
        $this->repository = $this->manager->getRepository($className);
        $this->alias = $alias;
        $this->builder = $this->repository->createQueryBuilder($alias);
    }

    /**
     * @return DB
     */
    public static function make(): DB
    {
        if (null === self::$instance) {
            // 使用临时变量实现双重检查锁定
            static $instanced = false;
            if (!$instanced) {
                $instanced = true;
                self::$instance = new self();
            }
        }
        return self::$instance;
    }

    /**
     * @param string $className
     * @param string $alias
     * @return DB
     */
    public static function table(string $className, string $alias = "e"): DB
    {
        if (null === self::$instance) {
            // 使用临时变量实现双重检查锁定
            static $instanced = false;
            if (!$instanced) {
                $instanced = true;
                self::$instance = new self($className, $alias);
            }
        }
        return self::$instance;
    }

    /**
     * @return EntityManager
     */
    public function manager():EntityManager
    {
        return $this->manager;
    }

    /**
     * @return EntityRepository
     */
    public function repository():EntityRepository
    {
        return $this->repository;
    }

    /**
     * @param array $data
     * @return mixed
     */
    public function update(array $data):mixed
    {
        $this->builder->update($this->className,$this->alias);
        foreach ($data as $key=>$value){
            if (str_contains(".",$key)){
                $field = $key;
            }else{
                $field = $this->alias.".".$key;
            }
            $this->builder->set($field,":".ltrim(strstr($field,"."),"."));
            $this->builder->setParameter($key,$value);
        }
        return $this->getQuery()->execute();
    }

    /**
     * @param \Closure $closure
     * @return $this
     */
    public function query(\Closure $closure):static
    {
        $closure($this->builder);
        return $this;
    }

    /**
     * @param string $from
     * @param string $alias
     * @param string|null $indexBy
     * @return $this
     */
    public function from(string $from, string $alias, string|null $indexBy = null):static
    {
        $this->builder->from($from,$alias,$indexBy);
        return $this;
    }

    /**
     * @param string $indexBy
     * @return $this
     * @throws QueryException
     */
    public function indexBy(string $indexBy):static
    {
        $this->builder->indexBy($this->alias,$indexBy);
        return $this;
    }

    public function orderBy(string|Expr\OrderBy $sort, string|null $order = null):static
    {
        $this->builder->orderBy($sort,$order);
        return $this;
    }

    /**
     * @return Query
     */
    public function getQuery():Query
    {
        return $this->builder->getQuery();
    }

    /**
     * @return mixed
     */
    public function toResult():mixed
    {
        return $this->getQuery()->getResult();
    }

    /**
     * @return mixed
     */
    public function get():mixed
    {
        return $this->toResult();
    }

    /**
     * @return mixed[]
     */
    public function toArray():array
    {
        return $this->getQuery()->getArrayResult();
    }


    /**
     * @param string $method
     * @param $args
     * @return $this
     */
    public function __call(string $method,$args):static
    {
        if (method_exists($this->builder,$method)){
            $this->builder->{$method}(...$args);
        }
        return $this;
    }

    public function toObject(){

    }
}